﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "CmdAddNode.h"
#include "Box3d.h"
#include "IElement.h"
#include "OwnerPtr.h"
#include "Matrix4d.h"
#include "IDocument.h"
#include "ElementId.h"
#include "ISelection.h"
#include "IUiDocument.h"
#include "IDirectShape.h"
#include "IGraphicsStyle.h"
#include "UiCommonDialog.h"
#include "IGenericElement.h"
#include "GcmpCommandState.h"
#include "IUserTransaction.h"
#include "IGraphicsZoomFree.h"
#include "GraphicsNodeUtils.h"
#include "IElementModelShape.h"
#include "IGraphicsNodeGroup.h"
#include "IGraphicsStyleData.h"
#include "IPureGraphicsElement.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsStyleManager.h"
#include "IUiDocumentViewManager.h"
#include "IElementBasicInformation.h"
#include "IGraphicsElementShapeReference.h"
#include "GcmpBuiltInCategoryUniIdentities.h"

#include "GRepCommandIds.h"
#include "CommandRegister.h"
#include "CmdGRepModuleEntry.h"
#include "GRepProbe/GrepProbeDlg.h"
#include "GRepProbe/GNodeUtility.h"
#include "GRepProbe/ObjectBrowser.h"
#include "GRepProbe/GNodeWrapper.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 

using namespace gcmp;
using namespace Sample;

REGISTER_COMMAND(CmdAddNode)

CmdAddNode::CmdAddNode()
    :CommandBase(ID_CMD_GREP_ADD_NODE)
{
}

bool CmdAddNode::IsEnabled() const
{
    if (!GcmpCommandState::IsInDocumentEnvironment())
    {
        return false; // 不在项目环境则退出
    }
    // 只有只选择了一个图元时才可以执行命令
    if (ISelection* pSelection = ISelection::Get())
    {
        std::unordered_set<ElementId, ElementIdHash> elemMaps = pSelection->GetSelectedElements();
        if (elemMaps.size() != 1)
        {
            return false;
        }
    }
    // 确认GRep图形表达浏览器初始化过
    ObjectBrowser* objectBrowser = ObjectBrowser::Get();
    if (!objectBrowser) { return false; }

    // 确认GRep图形表达浏览器里有源图元的GRep信息
    IGraphicsNode_Wrapper* pGNodeWrapper = objectBrowser->GetGNodeWrapper();
    if (!pGNodeWrapper) { return false; }

    // 确认GRep图形表达浏览器里的源Grep是可以修改的，并且的包含源GRep不为空
    return !pGNodeWrapper->IsReadOnly() && pGNodeWrapper->GetGraphicsNode() != nullptr;
}

bool CmdAddNode::IsVisible() const
{
    return GcmpCommandState::IsInDocumentEnvironment();
}

gcmp::OwnerPtr<gcmp::IAction> CmdAddNode::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IUiDocumentViewManager* pUiDocViewMgr = IUiDocumentViewManager::Get();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDocViewMgr, L"获取UI文档视图管理器失败",L"GDMP",L"2024-03-30");
    IUiDocument* pUiDoc = pUiDocViewMgr->GetCurrentUiDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDoc, L"获取UI文档失败",L"GDMP",L"2024-03-30");
    IDocument* pDoc = pUiDoc->GetDbDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"获取文档失败",L"GDMP",L"2024-03-30");

    // 下面获得选择图元的GRep，其中的源图形表达将要被添加到目标图形表达探针树中选择的节点
    ISelection* pSelection = ISelection::Get();
    std::unordered_set<ElementId, ElementIdHash> elemMaps = pSelection->GetSelectedElements();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(elemMaps.size() > 0, L"elemMaps为空",L"GDMP",L"2024-03-30");

    ElementId idSource = *elemMaps.begin();
    IElement* pElementSource = pDoc->GetElement(idSource);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementSource, L"pElementSource为空",L"GDMP",L"2024-03-30");
    IElementModelShape* pElementModelShapeSource = pElementSource->GetElementModelShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementModelShapeSource, L"pElementModelShapeSource为空",L"GDMP",L"2024-03-30");
    const IGraphicsElementShape* pGRepSource = pElementModelShapeSource->GetGraphicsElementShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGRepSource, L"pGRepSource为空",L"GDMP",L"2024-03-30");

    ObjectBrowser* pObjectBrowser = ObjectBrowser::Get();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pObjectBrowser, L"pObjectBrowser为空",L"GDMP",L"2024-03-30");

    // 获得目标图形表达浏览器中的节点，将要把选择图元的源GRep里的内容复制到这里
    IGraphicsNode_Wrapper* pGNodeWrapperTarget = pObjectBrowser->GetGNodeWrapper();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGNodeWrapperTarget, L"pGNodeWrapperTarget为空",L"GDMP",L"2024-03-30");

    IGraphicsNode* pGNodeTarget = pGNodeWrapperTarget->GetGraphicsNode();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGNodeTarget, L"pGNodeTarget为空",L"GDMP",L"2024-03-30");

    // 将要添加GRep的目标图元
    ElementId idTarget = pGNodeWrapperTarget->GetElementId();
    IElement* pElementTarget = pDoc->GetElement(idTarget);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementTarget, L"pElementTarget为空",L"GDMP",L"2024-03-30");

    // 将要增加GRep的目标图元可以是IDirectShape或IPureGraphicsElement。如果都不是，则退出。
    IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElementTarget);
    IDirectShape* pDirectShape = nullptr;
    IPureGraphicsElement* pPGE = quick_cast<IPureGraphicsElement>(pElementTarget);
    if (pGenericElement)
    {
        pDirectShape = quick_cast<IDirectShape>(pGenericElement->GetExternalObject());
    }
    if ((pDirectShape == nullptr) && (pPGE == nullptr))
    {
        UiCommonDialog::ShowMessageBox(L"提示", L"图形表达探针树中的图元不是IDirectShape或IPureGraphicsElement，因此无法添加图形节点", (int)UiCommonDialog::ButtonType::Yes);
        return nullptr;
    }

    if (idTarget == idSource)
    {
        UiCommonDialog::ShowMessageBox(L"提示", L"图形表达探针树中的目标图元和选择的源图元是同一个图元，因此无法添加图形节点", (int)UiCommonDialog::ButtonType::Yes);
        return nullptr;
    }

    // 下面代码寻找从当前目标图形节点和之上的图形节点中的第一个IGraphicsGroupBase集合图形节点，作为目标插入点
    IGraphicsNode* pTmpGNode = pGNodeTarget;
    IGraphicsGroupBase* pGGroupTarget = nullptr;
    while (true)
    {
        pGGroupTarget = quick_cast<IGraphicsGroupBase>(pTmpGNode);
        if (pGGroupTarget)
        {
            break;
        }
        else
        {
            IGraphicsNode_Wrapper* pParentWrapper = pGNodeWrapperTarget->GetParent();
            pTmpGNode = pParentWrapper->GetGraphicsNode();
        }
    }

    // 获得目标图形表达的根节点GRep
    IGraphicsNode_Wrapper* pGRepWrapperTarget = pGNodeWrapperTarget->GetRootParent();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGRepWrapperTarget, L"pGRepWrapperTarget为空",L"GDMP",L"2024-03-30");
    OwnerPtr<IGraphicsElementShape> opTargetCachedGRep = TransferOwnership(pGRepWrapperTarget->GetCachedGRep());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opTargetCachedGRep, L"opTargetCachedGRep为空",L"GDMP",L"2024-03-30");

    // 图形表达探针树中对应的目标图元可能移动过，下面代码根据缓存的GRep和最新GRep的包围盒，将缓存GRep移动到最新位置
    // IDirectShape和IPureGraphicsElement图元没有图元的位置信息，图元的旋转移动都是直接修改GRep。因此只能处理移动，无法处理旋转。
    {
        Box3d cachedTargetGRepBox = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(pDoc, opTargetCachedGRep.get());
        const IElementModelShape* pTargetLatestModelShape = pElementTarget->GetElementModelShape();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pTargetLatestModelShape, L"pTargetLatestModelShape为空",L"GDMP",L"2024-03-30");
        const IGraphicsElementShape* pTargetLatestGRep = pTargetLatestModelShape->GetGraphicsElementShape();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pTargetLatestGRep, L"pTargetLatestGRep为空",L"GDMP",L"2024-03-30");
        Box3d targetLatestGRepBox = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(pDoc, pTargetLatestGRep);

        if (!cachedTargetGRepBox.IsEmpty() && targetLatestGRepBox.IsEmpty())
        {
            Vector3d movedVector = (targetLatestGRepBox.GetCenter() - cachedTargetGRepBox.GetCenter());
            Matrix4d matrix;
            matrix.MakeTranslate(movedVector);
            opTargetCachedGRep->Transform(matrix);
        }
    }

    // 启动事务，添加图形表达，并设置给目标图元
    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, L"添加GNode");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opTransaction, L"opTransaction为空",L"GDMP",L"2024-03-30");

    // 对有些GNode需要设置GStyle，否则有DBG_WARN。目前先用下面的简单方式处理
    const IGraphicsStyleManager *pGraphicsStyleManager = pDoc->GetGraphicsStyleManager();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGraphicsStyleManager, L"pGraphicsStyleManager为空",L"GDMP",L"2024-03-30");
    ElementId styleId = pGraphicsStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_TEMP_STYLE);

    // 界面上有多种将选择源图元GRep添加到目标图元图形表达探针树中的方式
    // 下面将源图元GRep的内容复制到图形表达探针树中目标图元的集合节点位置
    AddNodeStyle addNodeStyle = CmdGRepModuleEntry::GetAddNodeStyle();
    if (addNodeStyle == AddNodeStyle::AsGGroup)     // 将源GRep内的内容作为IGraphicsNodeGroup添加
    {
        OwnerPtr<IGraphicsNodeGroup> opGNodeGroup = IGraphicsNodeGroup::Create();
        for (auto& itor : pGRepSource->GetChildren())
        {
            opGNodeGroup->AddChild(itor.get());
        }
        pGGroupTarget->AddChild(TransferOwnership(opGNodeGroup));
    }
    else if (addNodeStyle == AddNodeStyle::GraphicsElementReference) // 将源GRep作为几何共享方式添加到目标图元
    {
        Matrix4d matrix;
        matrix.MakeIdentity();
        OwnerPtr<IGraphicsElementShapeReference> opGNodeRef = IGraphicsElementShapeReference::Create(idSource, matrix);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opGNodeRef, L"opGNodeRef为空",L"GDMP",L"2024-03-30");
        pGGroupTarget->AddChild(TransferOwnership(opGNodeRef));
    }
    else    // 将源GRep下的子节点添加到目标图元中
    {
        for (auto& itor : pGRepSource->GetChildren())
        {
            pGGroupTarget->AddChild(itor.get());
        }
    }

    // 深克隆一份目标图元的GRep用于图形表达探针
    OwnerPtr<IGraphicsElementShape> opNewTargetCachedGRep = TransferOwnershipQuickCast<IGraphicsElementShape>(opTargetCachedGRep->Clone());

    // 根据目标图元是IPureGraphicsElement或IDirectShape去设置GRep
    if (pDirectShape)
    {
        pDirectShape->SetGraphicsElementShape(TransferOwnership(opTargetCachedGRep));
    }
    else if(pPGE)
    {
        IElementModelShape* pElemModelShape = pPGE->GetElementModelShape();
        pElemModelShape->SetGraphicsElementShape(TransferOwnership(opTargetCachedGRep));
    }

    opTransaction->Commit();

    // 图形表达探针浏览器清空
    pObjectBrowser->SetDataAndUpdate(nullptr);
    // 显示图形表达探针树窗口
    GrepProbeDlg* pGrepProbeDlg = GrepProbeDlg::GetOrCreate();
    pGrepProbeDlg->ShowDock();

    // 显示图形表达探针树显示添加过图形表达的新的GRep
    OwnerPtr<IGraphicsNode_Wrapper> opGNodeWrapper = 
        GNodeUtility::CreateGNodeWrapperTree(pDoc, idTarget, opNewTargetCachedGRep.get(), opNewTargetCachedGRep.get(), false, nullptr);
    pGrepProbeDlg->SetDisplayData(TransferOwnership(opGNodeWrapper));
    pGrepProbeDlg->GetDisplayData()->CacheGRep(TransferOwnership(opNewTargetCachedGRep));

    return nullptr;
}

